﻿using Microsoft.Extensions.Logging;
using Naruto.Redis.Interface;
using Naruto.Redis.Internal;
using Naruto.Redis.Object;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Naruto.Redis
{
    /// <summary>
    /// 
    /// </summary>
    public class RedLock : IRedLock
    {

        /// <summary>
        /// 延长锁时间的定时器
        /// </summary>
        private Timer _extendedLockTimer;
        /// <summary>
        /// 日志
        /// </summary>

        private readonly ILogger _logger;
        /// <summary>
        /// 
        /// </summary>
        private readonly IRedLockConnectionFactory _connectionFactory;

        /// <summary>
        /// 锁id
        /// </summary>
        public string LockId { get; }

        /// <summary>
        /// 资源名称
        /// </summary>
        public string ResourceName { get; }

        /// <summary>
        /// 过期时间
        /// </summary>
        public TimeSpan ExpireTime { get; }

        /// <summary>
        /// 是否成功 获取到锁
        /// </summary>
        public bool IsAcquired => Status == RedLockStatus.Acquired;

        /// <summary>
        /// 锁的状态
        /// </summary>
        public RedLockStatus Status { get; private set; }

        /// <summary>
        /// 重试次数
        /// </summary>
        private const int RetryCount = 3;

        /// <summary>
        /// 等待时间
        /// </summary>
        private TimeSpan WaitTime { get; }
        /// <summary>
        /// 轮询的延迟时间
        /// </summary>
        private TimeSpan DelayTime { get; }
        /// <summary>
        /// 默认的延迟等待时间
        /// </summary>
        private const long DefaultDelayTime = 100;

        /// <summary>
        /// 判断锁获取成功的服务数
        /// </summary>
        private int SuccessCount { get; set; }

        /// <summary>
        /// 锁
        /// </summary>
        private readonly SemaphoreSlim _lockExtended = new SemaphoreSlim(1);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="redLockConnectionFactory"></param>
        private RedLock(IRedLockConnectionFactory redLockConnectionFactory, ILogger logger, string resourceName, TimeSpan expireTime, TimeSpan waitTime, TimeSpan delayTime)
        {
            _connectionFactory = redLockConnectionFactory;
            _logger = logger;
            LockId = CreateUniKey();
            ResourceName = resourceName;
            ExpireTime = expireTime;
            WaitTime = waitTime;
            DelayTime = delayTime;
            if (DelayTime.TotalMilliseconds <= 0)
            {
                DelayTime = TimeSpan.FromMilliseconds(DefaultDelayTime);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="logger">日志</param>
        /// <param name="redLockConnectionFactory">锁连接</param>
        /// <param name="expireTime">过期时间</param>
        ///<param name="resourceName">资源名称</param>
        ///<param name="delayTime">轮询的延迟等待时间</param>
        ///<param name="waitTime">获取锁的整体等待时间</param>
        /// <returns></returns>
        public static async Task<RedLock> CreateAsync(IRedLockConnectionFactory redLockConnectionFactory, ILogger logger, string resourceName, TimeSpan expireTime, TimeSpan waitTime, TimeSpan delayTime)
        {
            //实例化对象
            var redLock = new RedLock(redLockConnectionFactory, logger, resourceName, expireTime, waitTime, delayTime);
            var multiplexer = await redLockConnectionFactory.CreateConnectionAsync().ConfigureAwait(false);
            if (multiplexer == null || multiplexer.Count <= 0)
            {
                throw new ArgumentNullException("redis服务不能为空");
            }
            //资源获取成功的依据
            redLock.SuccessCount = multiplexer.Count / 2 + 1;
            //执行锁
            await redLock.StartAsync().ConfigureAwait(false);
            return redLock;
        }
        /// <summary>
        /// 开启创建锁资源
        /// </summary>
        /// <returns></returns>
        private async Task StartAsync()
        {
            if (WaitTime.TotalMilliseconds > 0)
            {
                Stopwatch stopwatch = Stopwatch.StartNew();
                while (!IsAcquired && stopwatch.Elapsed <= WaitTime)
                {
                    //获取锁
                    await AcquiredAsync().ConfigureAwait(false);
                    if (!IsAcquired)
                        await Task.Delay(DelayTime).ConfigureAwait(false);
                }
                stopwatch.Stop();
            }
            else
                await AcquiredAsync();
            //开启新线程 定时延长锁时间
            if (IsAcquired)
            {
                AutoExtenedTime();
            }
        }


        /// <summary>
        /// 获取锁
        /// </summary>
        /// <returns></returns>
        protected virtual async Task AcquiredAsync()
        {
            //重试三次
            for (int i = 1; i <= RetryCount; i++)
            {
                Stopwatch stopwatch = Stopwatch.StartNew();

                //获取连接信息
                var multiplexer = await _connectionFactory.CreateConnectionAsync().ConfigureAwait(false);
                var tasks = multiplexer.Select(a => LockAsync(a));
                var statusList = await Task.WhenAll(tasks);

                stopwatch.Stop();
                //当有一半的数成功 并且 执行锁的消耗时间小于过期时间就代表锁获取成功
                if (statusList.Count(a => a == RedLockStatus.Acquired) >= SuccessCount && stopwatch.Elapsed.TotalMilliseconds < ExpireTime.TotalMilliseconds)
                {
                    Status = RedLockStatus.Acquired;
                    return;
                }
                Status = RedLockStatus.UnLock;
                //取消锁
                await ReleaseAsync().ConfigureAwait(false);

                await Task.Delay(TimeSpan.FromMilliseconds(50)).ConfigureAwait(false);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async ValueTask DisposeAsync()
        {
            await DisposeAsync(true);
        }

        private void AutoExtenedTime()
        {
            //设置多久执行此定时器
            var time = ExpireTime.TotalMilliseconds / 2;
            _logger.LogTrace("开启定时服务，延长锁过期时间");
            //开启定时器
            _extendedLockTimer = new Timer((obj) => ExtendedTimeAsync(), null, (int)time, (int)time);
        }
        /// <summary>
        /// 扩展锁时间
        /// </summary>
        private async void ExtendedTimeAsync()
        {
            _logger.LogTrace("正在执行延长锁过期时间");
            try
            {
                await _lockExtended.WaitAsync().ConfigureAwait(false);
                //获取连接
                var multiplexer = await _connectionFactory.CreateConnectionAsync().ConfigureAwait(false);
                var tasks = multiplexer.Select(a => ExtendedTimeAsync(a));
                await Task.WhenAll(tasks);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "ExtendedTimeAsync");
            }
            finally
            {
                _lockExtended.Release();
            }
        }

        private async Task ExtendedTimeAsync(ConnectionMultiplexer connectionMultiplexer)
        {
            try
            {
                await connectionMultiplexer.GetDatabase().ScriptEvaluateAsync(LuaResources.ExtendedTimeScript, new StackExchange.Redis.RedisKey[] {
                    ResourceName
                }, new RedisValue[] {
                    LockId,
                    ExpireTime.TotalMilliseconds
                }, CommandFlags.DemandMaster).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "ExtendedTimeAsync 执行lua");
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="isDispose"></param>
        /// <returns></returns>
        protected virtual async ValueTask DisposeAsync(bool isDispose)
        {
            if (isDispose)
            {
                //释放锁
                await ReleaseAsync();
                _extendedLockTimer?.Change(Timeout.Infinite, Timeout.Infinite);
                await _extendedLockTimer?.DisposeAsync().AsTask();
                _extendedLockTimer = null;
                Status = RedLockStatus.UnLock;
                GC.SuppressFinalize(this);
            }
        }

        /// <summary>
        /// 释放锁资源
        /// </summary>
        /// <returns></returns>
        protected virtual async Task ReleaseAsync()
        {
            //获取连接信息
            var multiplexer = await _connectionFactory.CreateConnectionAsync();
            var tasks = multiplexer.Select(a => UnLockAsync(a));
            await Task.WhenAll(tasks);
        }

        /// <summary>
        /// 加锁
        /// </summary>
        /// <param name="connectionMultiplexer"></param>
        /// <returns></returns>
        private async Task<RedLockStatus> LockAsync(ConnectionMultiplexer connectionMultiplexer)
        {
            try
            {
                //锁定资源
                var result = await connectionMultiplexer.GetDatabase().StringSetAsync(ResourceName, LockId, ExpireTime, When.NotExists, CommandFlags.DemandMaster).ConfigureAwait(false);
                return result ? RedLockStatus.Acquired : RedLockStatus.UnLock;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "执行锁失败");
                return RedLockStatus.Error;
            }
        }
        /// <summary>
        /// 取消锁
        /// </summary>
        /// <param name="connectionMultiplexer"></param>
        /// <returns></returns>
        private async Task UnLockAsync(ConnectionMultiplexer connectionMultiplexer)
        {
            try
            {
                await connectionMultiplexer.GetDatabase().ScriptEvaluateAsync(LuaResources.UnLockScript, new StackExchange.Redis.RedisKey[] {
                    ResourceName
                }, new RedisValue[] {
                    LockId
                }, CommandFlags.DemandMaster);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"释放锁失败，EndPoints{connectionMultiplexer.GetEndPoints().FirstOrDefault().ToString()}");
            }
        }

        /// <summary>
        /// 生成一个唯一的密钥
        /// </summary>
        /// <returns></returns>
        private string CreateUniKey() => Guid.NewGuid().ToString();


    }
}
